/********************************************************************

GlControl implementation file

Write Date: 2011/07/14
Author: Deng Nianchen
>>>> Encapsulate most of the glut callback functions include:
>>>> display function
>>>> key event functions
>>>> mouse event functions
>>>> reshape event functions
>>>> timer event functions

Modify Date: 2011/03/24
Author: Deng Nianchen
>>>> Add mainLoop () function (idle callback function)
>>>> Add to GlBase namespace
>>>> Use GlEvent to encapsulate event data

*********************************************************************/
#include "GlApplication.h"

// Pointer of global control object
GlBase::GlApplication* _pControl = GlBase::GlApplication::getSingletonPtr ();

// Global mouse state
GlBase::GlMouseButtonState _mouseButtonState (false, false, false);

using GlBase::GlEvent;

/************************************************************************/
/* Callback functions for glut framework                                */
/************************************************************************/

void handle_idle ();
void handle_display ();
void handle_reshape (int width, int height);
void handle_timer (int val);
void handle_key (unsigned char key, int x, int y);
void handle_special (int key, int x, int y);
void handle_mouse (int button, int state, int x, int y);
void handle_motion (int x, int y);
void handle_passive_motion (int x, int y);

/************************************************************************/
/* Callback function implementations                                    */
/************************************************************************/

void handle_idle()
{
  _pControl->mainLoop ();
}

void handle_display ()
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();
  _pControl->drawEvent ();
  glutSwapBuffers ();
}

void handle_reshape (int width, int height)
{
  _pControl->reshapeWindowEvent (width, height);
}

void handle_timer (int val)
{
  _pControl->timerEvent ();
  if (_pControl->isTimerRegistered ())
    _pControl->registerTimer ();
  glutPostRedisplay ();
}

void handle_key (unsigned char key, int x, int y)
{
  GlEvent e;
  e.setType (GlEvent::KEY_DOWN);
  e.setKey (key);
  e.setMouseButtonState (_mouseButtonState);
  e.setMousePosition (x, y);
  _pControl->keyPressEvent (e);
}

void handle_special (int key, int x, int y)
{
  GlEvent e;
  e.setType (GlEvent::KEY_DOWN);
  e.setKey (-key);
  e.setMouseButtonState (_mouseButtonState);
  e.setMousePosition (x, y);
  _pControl->keyPressEvent (e);
}

void handle_mouse (int button, int state, int x, int y)
{
  if (state == GLUT_DOWN)
    _mouseButtonState[button] = true;
  else
    _mouseButtonState[button] = false;
  GlEvent e;
  e.setType (state == GLUT_DOWN ? GlEvent::MOUSE_DOWN : GlEvent::MOUSE_UP);
  e.setMouseButtonState (_mouseButtonState);
  e.setMousePosition (x, y);
  _pControl->mouseEvent (e);
}

void handle_motion (int x, int y)
{
  GlEvent e;
  e.setType (GlEvent::MOUSE_MOVE);
  e.setMouseButtonState (_mouseButtonState);
  e.setMousePosition (x, y);
  _pControl->mouseEvent (e);
}

void handle_passive_motion (int x, int y)
{
  handle_motion (x, y);
}

/************************************************************************/
/* GlControl function implementations                                   */
/************************************************************************/

_GLBASE_BEGIN

GlApplication::GlApplication () : _timerDuration (0) {}

void GlApplication::initialize (int argc, char** argv)
{
	glutInit (&argc, argv);
}

void GlApplication::registerTimer (int millisecond)
{
  if (millisecond != 0)
    _timerDuration = millisecond;
  glutTimerFunc (_timerDuration, handle_timer, 0);
}

void GlApplication::createWindow (uint mode, const char* title,
  bool isFullScreen, int width /*= 800*/, int height /*= 600*/,
  int posX /*= 0*/, int posY /*= 0*/)
{
  glutInitDisplayMode (mode);
  glutInitWindowPosition (posX, posY);
  glutInitWindowSize (width, height);
  glutCreateWindow (title);
  if (isFullScreen)
    glutFullScreen ();
}

void GlApplication::run()
{
  _pControl = this;
  _pControl->initialEvent ();

  glutIdleFunc (handle_idle);
  glutDisplayFunc (handle_display);
  glutReshapeFunc (handle_reshape);
  glutKeyboardFunc (handle_key);
  glutSpecialFunc (handle_special);
  glutMouseFunc (handle_mouse);
  glutMotionFunc (handle_motion);
  glutPassiveMotionFunc (handle_passive_motion);

  glutMainLoop ();
}

_GLBASE_END